/*-
 * Copyright 2012 Guram Dukashvili
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
//---------------------------------------------------------------------------
#include "sys.h"
#include "hash.h"
#include "int.h"
//---------------------------------------------------------------------------
int32_t bsint_get_i32(const byte_size_int * bsint,uintptr_t byte_size)
{
  union {
    int32_t v;
    int8_t b[4];
  } a;
  if( byte_size == 4 ) return *(int32_t *) bsint;
  a.v = bsint->dummy[byte_size - 1] >> 7;
  switch( byte_size ){
#if COMPILE_RUNTIME_CHECKS
    default : abort();
#endif
    case 8  :
    case 7  :
    case 6  :
    case 5  :
    case 4  :
    case 3  : a.b[2] = bsint->dummy[2];
    case 2  : a.b[1] = bsint->dummy[1];
    case 1  : a.b[0] = bsint->dummy[0];
  }
  return a.v;
}
//---------------------------------------------------------------------------
uint32_t bsint_get_u32(const byte_size_int * bsint,uintptr_t byte_size)
{
  union {
    uint32_t v;
    uint8_t b[4];
  } a;
  if( byte_size == 4 ) return *(uint32_t *) bsint;
  a.v = 0;
  switch( byte_size ){
#if COMPILE_RUNTIME_CHECKS
    default : abort();
#endif
    case 8  :
    case 7  :
    case 6  :
    case 5  :
    case 4  :
    case 3  : a.b[2] = bsint->dummy[2];
    case 2  : a.b[1] = bsint->dummy[1];
    case 1  : a.b[0] = bsint->dummy[0];
  }
  return a.v;
}
//---------------------------------------------------------------------------
int64_t bsint_get_i64(const byte_size_int * bsint,uintptr_t byte_size)
{
  union {
    int64_t v;
    int8_t b[8];
  } a;
  if( byte_size == 8 ) return *(int64_t *) bsint;
  a.v = bsint->dummy[byte_size - 1] >> 7;
  switch( byte_size ){
#if COMPILE_RUNTIME_CHECKS
    default : abort();
#endif
    case 8  :
    case 7  : a.b[6] = bsint->dummy[6];
    case 6  : a.b[5] = bsint->dummy[5];
    case 5  : a.b[4] = bsint->dummy[4];
    case 4  : a.b[3] = bsint->dummy[3];
    case 3  : a.b[2] = bsint->dummy[2];
    case 2  : a.b[1] = bsint->dummy[1];
    case 1  : a.b[0] = bsint->dummy[0];
  }
  return a.v;
}
//---------------------------------------------------------------------------
uint64_t bsint_get_u64(const byte_size_int * bsint,uintptr_t byte_size)
{
  union {
    uint64_t v;
    uint8_t b[8];
  } a;
  if( byte_size == 8 ) return *(uint64_t *) bsint;
  a.v = 0;
  switch( byte_size ){
#if COMPILE_RUNTIME_CHECKS
    default : abort();
#endif
    case 8  :
    case 7  : a.b[6] = bsint->dummy[6];
    case 6  : a.b[5] = bsint->dummy[5];
    case 5  : a.b[4] = bsint->dummy[4];
    case 4  : a.b[3] = bsint->dummy[3];
    case 3  : a.b[2] = bsint->dummy[2];
    case 2  : a.b[1] = bsint->dummy[1];
    case 1  : a.b[0] = bsint->dummy[0];
  }
  return a.v;
}
//---------------------------------------------------------------------------
uint64_t bsint_get_be64toh(const byte_size_int * bsint,uintptr_t byte_size)
{
  union {
    uint64_t v;
    uint8_t b[8];
  } a;
  if( byte_size == 8 ) return be64dec(bsint);
  a.v = 0;
#if BYTE_ORDER == LITTLE_ENDIAN
  switch( byte_size ){
#if COMPILE_RUNTIME_CHECKS
    default : abort();
#endif
    case 8  :
    case 7  :
      a.b[6] = bsint->dummy[0];
      a.b[5] = bsint->dummy[1];
      a.b[4] = bsint->dummy[2];
      a.b[3] = bsint->dummy[3];
      a.b[2] = bsint->dummy[4];
      a.b[1] = bsint->dummy[5];
      a.b[0] = bsint->dummy[6];
      break;
    case 6  :
      a.b[5] = bsint->dummy[0];
      a.b[4] = bsint->dummy[1];
      a.b[3] = bsint->dummy[2];
      a.b[2] = bsint->dummy[3];
      a.b[1] = bsint->dummy[4];
      a.b[0] = bsint->dummy[5];
      break;
    case 5  :
      a.b[4] = bsint->dummy[0];
      a.b[3] = bsint->dummy[1];
      a.b[2] = bsint->dummy[2];
      a.b[1] = bsint->dummy[3];
      a.b[0] = bsint->dummy[4];
      break;
    case 4  :
      a.b[3] = bsint->dummy[0];
      a.b[2] = bsint->dummy[1];
      a.b[1] = bsint->dummy[2];
      a.b[0] = bsint->dummy[3];
      break;
    case 3  :
      a.b[2] = bsint->dummy[0];
      a.b[1] = bsint->dummy[1];
      a.b[0] = bsint->dummy[2];
      break;
    case 2  :
      a.b[1] = bsint->dummy[0];
      a.b[0] = bsint->dummy[1];
      break;
    case 1  : a.b[0] = bsint->dummy[0];
  }
#else
  memcpy(a.b + 8 - byte_size,bsint->dummy,byte_size);
#endif
  return a.v;
}
//---------------------------------------------------------------------------
intptr_t bsint_get_iptr(const byte_size_int * bsint,uintptr_t byte_size)
{
  union {
    intptr_t v;
    int8_t b[SIZEOF_INTPTR_T];
  } a;
  if( byte_size == SIZEOF_INTPTR_T ) return *(intptr_t *) bsint;
  a.v = bsint->dummy[byte_size - 1] >> 7;
  switch( byte_size ){
#if COMPILE_RUNTIME_CHECKS
    default : abort();
#endif
#if SIZEOF_INTPTR_T == 8
    case 8  : a.b[7] = bsint->dummy[7];
    case 7  : a.b[6] = bsint->dummy[6];
    case 6  : a.b[5] = bsint->dummy[5];
    case 5  : a.b[4] = bsint->dummy[4];
#elif SIZEOF_INTPTR_T == 4
    case 8  :
    case 7  :
    case 6  :
    case 5  :
#else
#error must be implemented
#endif
    case 4  : a.b[3] = bsint->dummy[3];
    case 3  : a.b[2] = bsint->dummy[2];
    case 2  : a.b[1] = bsint->dummy[1];
    case 1  : a.b[0] = bsint->dummy[0];
  }
  return a.v;
}
//---------------------------------------------------------------------------
uintptr_t bsint_get_uptr(const byte_size_int * bsint,uintptr_t byte_size)
{
  union {
    uintptr_t v;
    uint8_t b[SIZEOF_INTPTR_T];
  } a;
  if( byte_size == SIZEOF_INTPTR_T ) return *(uintptr_t *) bsint;
  a.v = 0;
  switch( byte_size ){
#if COMPILE_RUNTIME_CHECKS
    default : abort();
#endif
#if SIZEOF_INTPTR_T == 8
    case 8  : a.b[7] = bsint->dummy[7];
    case 7  : a.b[6] = bsint->dummy[6];
    case 6  : a.b[5] = bsint->dummy[5];
    case 5  : a.b[4] = bsint->dummy[4];
#elif SIZEOF_INTPTR_T == 4
    case 8  :
    case 7  :
    case 6  :
    case 5  :
#else
#error must be implemented
#endif
    case 4  : a.b[3] = bsint->dummy[3];
    case 3  : a.b[2] = bsint->dummy[2];
    case 2  : a.b[1] = bsint->dummy[1];
    case 1  : a.b[0] = bsint->dummy[0];
  }
  return a.v;
}
//---------------------------------------------------------------------------
uint32_t bsint_hash(const byte_size_int * bsint,uintptr_t byte_size)
{
  uint32_t h = HASH_PRIME;
  switch( byte_size ){
#if COMPILE_RUNTIME_CHECKS
    default : abort();
#endif
    case 8  : h = (h << 5) + h + (uint8_t) bsint->dummy[7];
    case 7  : h = (h << 5) + h + (uint8_t) bsint->dummy[6];
    case 6  : h = (h << 5) + h + (uint8_t) bsint->dummy[5];
    case 5  : h = (h << 5) + h + (uint8_t) bsint->dummy[4];
    case 4  : h = (h << 5) + h + (uint8_t) bsint->dummy[3];
    case 3  : h = (h << 5) + h + (uint8_t) bsint->dummy[2];
    case 2  : h = (h << 5) + h + (uint8_t) bsint->dummy[1];
    case 1  : h = (h << 5) + h + (uint8_t) bsint->dummy[0];
  }
  return h;
}
//---------------------------------------------------------------------------
bool bsint_is_zero(const byte_size_int * bsint,uintptr_t byte_size)
{
  switch( byte_size ){
#if COMPILE_RUNTIME_CHECKS
    default : abort();
#endif
    case 8  : return *(uint64_t *) bsint == 0;
    case 7  : return *(uint32_t *) bsint == 0 && *(uint16_t *) (bsint->dummy + 4) == 0 && bsint->dummy[6] == 0;
    case 6  : return *(uint32_t *) bsint == 0 && *(uint16_t *) (bsint->dummy + 4) == 0;
    case 5  : return *(uint32_t *) bsint == 0 && bsint->dummy[4] == 0;
    case 4  : return *(uint32_t *) bsint == 0;
    case 3  : return *(uint16_t *) bsint->dummy == 0 && bsint->dummy[2] == 0;
    case 2  : return *(uint16_t *) bsint->dummy == 0;
    case 1  : return bsint->dummy[0] == 0;
  }
#ifndef _MSC_VER
  return false;
#endif
}
//---------------------------------------------------------------------------
void bsint_set_i32(byte_size_int * bsint,uintptr_t byte_size,int32_t v)
{
  if( byte_size == 4 ) *(int32_t *) bsint = v;
  else switch( byte_size ){
#if COMPILE_RUNTIME_CHECKS
    default : abort();
#endif
    case 8  : bsint->dummy[8] = ((const int8_t *) &v) [3] >> 7;
    case 7  : bsint->dummy[7] = ((const int8_t *) &v) [3] >> 7;
    case 6  : bsint->dummy[6] = ((const int8_t *) &v) [3] >> 7;
    case 5  : bsint->dummy[4] = ((const int8_t *) &v) [3] >> 7;
    case 4  : bsint->dummy[3] = ((const uint8_t *) &v) [3];
    case 3  : bsint->dummy[2] = ((const uint8_t *) &v) [2];
    case 2  : bsint->dummy[1] = ((const uint8_t *) &v) [1];
    case 1  : bsint->dummy[0] = ((const uint8_t *) &v) [0];
  }
}
//---------------------------------------------------------------------------
void bsint_set_u32(byte_size_int * bsint,uintptr_t byte_size,uint32_t v)
{
  if( byte_size == 4 ) *(uint32_t *) bsint = v;
  else switch( byte_size ){
#if COMPILE_RUNTIME_CHECKS
    default : abort();
#endif
    case 8  : bsint->dummy[7] = 0;
    case 7  : bsint->dummy[6] = 0;
    case 6  : bsint->dummy[5] = 0;
    case 5  : bsint->dummy[4] = 0;
    case 4  : bsint->dummy[3] = ((const uint8_t *) &v) [3];
    case 3  : bsint->dummy[2] = ((const uint8_t *) &v) [2];
    case 2  : bsint->dummy[1] = ((const uint8_t *) &v) [1];
    case 1  : bsint->dummy[0] = ((const uint8_t *) &v) [0];
  }
}
//---------------------------------------------------------------------------
void bsint_set_i64(byte_size_int * bsint,uintptr_t byte_size,int64_t v)
{
  if( byte_size == 8 ) *(int64_t *) bsint = v;
  else switch( byte_size ){
#if COMPILE_RUNTIME_CHECKS
    default : abort();
#endif
    case 8  :
    case 7  : bsint->dummy[7] = ((const uint8_t *) &v) [6];
    case 6  : bsint->dummy[6] = ((const uint8_t *) &v) [5];
    case 5  : bsint->dummy[4] = ((const uint8_t *) &v) [4];
    case 4  : bsint->dummy[3] = ((const uint8_t *) &v) [3];
    case 3  : bsint->dummy[2] = ((const uint8_t *) &v) [2];
    case 2  : bsint->dummy[1] = ((const uint8_t *) &v) [1];
    case 1  : bsint->dummy[0] = ((const uint8_t *) &v) [0];
  }
}
//---------------------------------------------------------------------------
void bsint_set_u64(byte_size_int * bsint,uintptr_t byte_size,uint64_t v)
{
  if( byte_size == 8 ) *(uint64_t *) bsint = v;
  else switch( byte_size ){
#if COMPILE_RUNTIME_CHECKS
    default : abort();
#endif
    case 8  :
    case 7  : bsint->dummy[6] = ((const uint8_t *) &v) [6];
    case 6  : bsint->dummy[5] = ((const uint8_t *) &v) [5];
    case 5  : bsint->dummy[4] = ((const uint8_t *) &v) [4];
    case 4  : bsint->dummy[3] = ((const uint8_t *) &v) [3];
    case 3  : bsint->dummy[2] = ((const uint8_t *) &v) [2];
    case 2  : bsint->dummy[1] = ((const uint8_t *) &v) [1];
    case 1  : bsint->dummy[0] = ((const uint8_t *) &v) [0];
  }
}
//---------------------------------------------------------------------------
void bsint_set_htobe64(byte_size_int * bsint,uintptr_t byte_size,uint64_t v)
{
#if BYTE_ORDER == LITTLE_ENDIAN
  if( byte_size == 8 ) be64enc(bsint,v);
  else switch( byte_size ){
#if COMPILE_RUNTIME_CHECKS
    default : abort();
#endif
    case 8  :
    case 7  :
      bsint->dummy[6] = ((const uint8_t *) &v) [0];
      bsint->dummy[5] = ((const uint8_t *) &v) [1];
      bsint->dummy[4] = ((const uint8_t *) &v) [2];
      bsint->dummy[3] = ((const uint8_t *) &v) [3];
      bsint->dummy[2] = ((const uint8_t *) &v) [4];
      bsint->dummy[1] = ((const uint8_t *) &v) [5];
      bsint->dummy[0] = ((const uint8_t *) &v) [6];
      break;
    case 6  :
      bsint->dummy[5] = ((const uint8_t *) &v) [0];
      bsint->dummy[4] = ((const uint8_t *) &v) [1];
      bsint->dummy[3] = ((const uint8_t *) &v) [2];
      bsint->dummy[2] = ((const uint8_t *) &v) [3];
      bsint->dummy[1] = ((const uint8_t *) &v) [4];
      bsint->dummy[0] = ((const uint8_t *) &v) [5];
      break;
    case 5  :
      bsint->dummy[4] = ((const uint8_t *) &v) [0];
      bsint->dummy[3] = ((const uint8_t *) &v) [1];
      bsint->dummy[2] = ((const uint8_t *) &v) [2];
      bsint->dummy[1] = ((const uint8_t *) &v) [3];
      bsint->dummy[0] = ((const uint8_t *) &v) [4];
      break;
    case 4  :
      bsint->dummy[3] = ((const uint8_t *) &v) [0];
      bsint->dummy[2] = ((const uint8_t *) &v) [1];
      bsint->dummy[1] = ((const uint8_t *) &v) [2];
      bsint->dummy[0] = ((const uint8_t *) &v) [3];
      break;
    case 3  :
      bsint->dummy[2] = ((const uint8_t *) &v) [0];
      bsint->dummy[1] = ((const uint8_t *) &v) [1];
      bsint->dummy[0] = ((const uint8_t *) &v) [2];
      break;
    case 2  :
      bsint->dummy[1] = ((const uint8_t *) &v) [0];
      bsint->dummy[0] = ((const uint8_t *) &v) [1];
      break;
    case 1  :
      bsint->dummy[0] = ((const uint8_t *) &v) [0];
  }
#else
  memcpy(bsint,(const uint8_t *) &v + 8 - byte_size,byte_size);
#endif
}
//---------------------------------------------------------------------------
void bsint_set_iptr(byte_size_int * bsint,uintptr_t byte_size,intptr_t v)
{
  if( byte_size == SIZEOF_INTPTR_T ) *(intptr_t *) bsint->dummy = v;
  else switch( byte_size ){
#if COMPILE_RUNTIME_CHECKS
    default : abort();
#endif
#if SIZEOF_INTPTR_T == 8
    case 8  : bsint->dummy[7] = ((const uint8_t *) &v) [7];
    case 7  : bsint->dummy[6] = ((const uint8_t *) &v) [6];
    case 6  : bsint->dummy[5] = ((const uint8_t *) &v) [5];
    case 5  : bsint->dummy[4] = ((const uint8_t *) &v) [4];
#elif SIZEOF_INTPTR_T == 4
    case 8  : bsint->dummy[7] = ((const int8_t *) &v) [7] >> 7;
    case 7  : bsint->dummy[6] = ((const int8_t *) &v) [6] >> 7;
    case 6  : bsint->dummy[5] = ((const int8_t *) &v) [5] >> 7;
    case 5  : bsint->dummy[4] = ((const int8_t *) &v) [4] >> 7;
#else
#error must be implemented
#endif
    case 4  : bsint->dummy[3] = ((const uint8_t *) &v) [3];
    case 3  : bsint->dummy[2] = ((const uint8_t *) &v) [2];
    case 2  : bsint->dummy[1] = ((const uint8_t *) &v) [1];
    case 1  : bsint->dummy[0] = ((const uint8_t *) &v) [0];
  }
}
//---------------------------------------------------------------------------
void bsint_set_uptr(byte_size_int * bsint,uintptr_t byte_size,uintptr_t v)
{
  if( byte_size == SIZEOF_INTPTR_T ) *(uintptr_t *) bsint = v;
  else switch( byte_size ){
#if COMPILE_RUNTIME_CHECKS
    default : abort();
#endif
#if SIZEOF_INTPTR_T == 8
    case 8  : bsint->dummy[7] = ((const uint8_t *) &v) [7];
    case 7  : bsint->dummy[6] = ((const uint8_t *) &v) [6];
    case 6  : bsint->dummy[5] = ((const uint8_t *) &v) [5];
    case 5  : bsint->dummy[4] = ((const uint8_t *) &v) [4];
#elif SIZEOF_INTPTR_T == 4
    case 8  : bsint->dummy[7] = 0;
    case 7  : bsint->dummy[6] = 0;
    case 6  : bsint->dummy[5] = 0;
    case 5  : bsint->dummy[4] = 0;
#else
#error must be implemented
#endif
    case 4  : bsint->dummy[3] = ((const uint8_t *) &v) [3];
    case 3  : bsint->dummy[2] = ((const uint8_t *) &v) [2];
    case 2  : bsint->dummy[1] = ((const uint8_t *) &v) [1];
    case 1  : bsint->dummy[0] = ((const uint8_t *) &v) [0];
  }
}
//---------------------------------------------------------------------------
void bsint_print(FILE * stream,const byte_size_int * bsint,uintptr_t byte_size)
{
  uintptr_t i;
  for( i = 0; i < byte_size; i++ )
    fprintf(stream,"%02X",(unsigned) ((const uint8_t *) bsint) [i]);
}
//---------------------------------------------------------------------------
